#include <retro.h>
#include "retro.creator.h"

using namespace Retro::Creator;

Hierarchy::Hierarchy()
{
}

Hierarchy::~Hierarchy()
{
  close();
}

VOID    Hierarchy::close()
{
  for (UINT i = 0; i < mPaths.getLength(); ++i)
  {
    delete[] mPaths[i]->name;
    delete[] mPaths[i];
  }

  for (UINT i = 0; i < mItems.getLength(); ++i)
  {
    delete[] mItems[i];
  }

  mPaths.empty();
  mItems.empty();
}

BOOL    Hierarchy::load(PCTSTR iFilename)
{
  BOOL wResult = TRUE;

  HANDLE wFile = CreateFile(
    iFilename,
    GENERIC_READ,
    0,
    NULL,
    OPEN_EXISTING,
    FILE_ATTRIBUTE_NORMAL,
    NULL);

  if (wFile == INVALID_HANDLE_VALUE)
  {
    wResult = FALSE;
  }
  else
  {
    DWORD wRead       = 0;
    DWORD wReadTotal  = 0;
    DWORD wSize       = 0;
    UINT  wItemCount  = 0;

    close();

    ReadFile(wFile, &wItemCount, sizeof(wItemCount), &wRead, NULL);

    if (wRead == sizeof(wItemCount) && wItemCount > 0)
    {
      mPaths.setSize(wItemCount);

      do
      {
        HierarchyPath* wPath = new HierarchyPath;
        UINT wPathLength;

        ReadFile(wFile, wPath, sizeof(HierarchyHeader), &wRead, NULL);
        ReadFile(wFile, &wPathLength, sizeof(wPathLength), &wRead, NULL);

        if (wPathLength && wPathLength < 1000)
        {
          wPath->name = new TCHAR[wPathLength + 1];
          wPath->name[wPathLength] = '\0';

          wSize       = wPathLength * sizeof(TCHAR);
          wReadTotal  = 0;

          while (wReadTotal < wSize)
          {
            if (!ReadFile(
              wFile,
              (BYTE*)wPath->name + wReadTotal,
              (DWORD)(wSize - wReadTotal),
              &wRead,
              NULL) || wRead != wSize)
            {
              wResult = FALSE;
              break;
            }

            wReadTotal += wRead;
          }

          if (wReadTotal == wSize)
            mPaths.add(wPath);
        }
      }
      while (--wItemCount && wResult);
    }

    ReadFile(wFile, &wItemCount, sizeof(wItemCount), &wRead, NULL);

    if (wRead == sizeof(wItemCount) && wItemCount > 0)
    {
      mItems.setSize(wItemCount);

      do
      {
        HierarchyItem* wItem = new HierarchyItem;

        wSize       = sizeof(HierarchyItem);
        wReadTotal  = 0;

        while (wReadTotal < wSize)
        {
          if (!ReadFile(
            wFile,
            (BYTE*)wItem + wReadTotal,
            (DWORD)(wSize - wReadTotal),
            &wRead,
            NULL) || wRead != wSize)
          {
            wResult = FALSE;
            break;
          }

          wReadTotal += wRead;
        }

        if (wReadTotal == wSize)
          mItems.add(wItem);
      }
      while (--wItemCount && wResult);
    }

    CloseHandle(wFile);
  }

  return wResult;
}

BOOL    Hierarchy::save(PCTSTR iFilename)
{
  BOOL wResult = TRUE;

  HANDLE wFile = CreateFile(
    iFilename,
    GENERIC_WRITE,
    0,
    NULL,
    CREATE_ALWAYS,
    FILE_ATTRIBUTE_NORMAL,
    NULL);

  if (wFile == INVALID_HANDLE_VALUE)
  {
    wResult = FALSE;
  }
  else
  {
    DWORD wWritten;
    DWORD wWrittenTotal;
    DWORD wSize;
    UINT  wLength;

    wLength = mPaths.getLength();
    WriteFile(wFile, &wLength, sizeof(UINT), &wWritten, NULL);

    for (UINT i = 0; i < mPaths.getLength() && wResult; ++i)
    {
      UINT wPathLength = (UINT)_tcslen(mPaths[i]->name);

      WriteFile(wFile, mPaths[i], sizeof(HierarchyHeader), &wWritten, NULL);
      WriteFile(wFile, &wPathLength, sizeof(UINT), &wWritten, NULL);

      wSize         = wPathLength * sizeof(TCHAR);
      wWrittenTotal = 0;

      while (wWrittenTotal < wSize)
      {
        if (!WriteFile(
          wFile,
          (BYTE*)mPaths[i]->name + wWrittenTotal,
          (DWORD)(wSize - wWrittenTotal),
          &wWritten,
          NULL) || wWritten != wSize)
        {
          wResult = FALSE;
          break;
        }

        wWrittenTotal += wWritten;
      }
    }

    wLength = mItems.getLength();
    WriteFile(wFile, &wLength, sizeof(UINT), &wWritten, NULL);

    for (UINT i = 0; i < mItems.getLength() && wResult; ++i)
    {
      wSize         = sizeof(HierarchyItem);
      wWrittenTotal = 0;

      while (wWrittenTotal < wSize)
      {
        if (!WriteFile(
          wFile,
          (BYTE*)mItems[i] + wWrittenTotal,
          (DWORD)(wSize - wWrittenTotal),
          &wWritten,
          NULL) || wWritten != wSize)
        {
          wResult = FALSE;
          break;
        }

        wWrittenTotal += wWritten;
      }
    }

    CloseHandle(wFile);
  }

  return wResult;
}

VOID    Hierarchy::setItemPath(
  INT iResourceID,
  INT iPathID,
  Data::ResourceType iType
  )
{
  for (UINT i = 0; i < mItems.getLength(); ++i)
  {
    if (mItems[i]->resourceID == iResourceID && mItems[i]->type == iType)
    {
      mItems[i]->pathID = iPathID;
      return;
    }
  }

  HierarchyItem* wItem = new HierarchyItem;

  wItem->resourceID = iResourceID;
  wItem->pathID     = iPathID;
  wItem->type       = iType;

  mItems.add(wItem);
}

VOID    Hierarchy::unsetItemPath(
  INT iResourceID,
  Data::ResourceType iType
  )
{
  for (UINT i = 0; i < mItems.getLength(); ++i)
  {
    if (mItems[i]->resourceID == iResourceID && mItems[i]->type == iType)
    {
      delete mItems[i];
      mItems.remove(i);
      return;
    }
  }
}

const HierarchyPath* Hierarchy::getItemPath(
  INT iResourceID,
  Data::ResourceType iType
  )
{
  for (UINT i = 0; i < mItems.getLength(); ++i)
  {
    if (mItems[i]->resourceID == iResourceID && mItems[i]->type == iType)
    {
      for (UINT j = 0; j < mPaths.getLength(); ++j)
      {
        if (mPaths[j]->pathID == mItems[i]->pathID)
          return mPaths[j];
      }
    }
  }

  return NULL;
}

VOID            Hierarchy::removePath(
  INT iPathID
  )
{
  for (UINT i = 0; i < mPaths.getLength(); ++i)
  {
    if (mPaths[i]->pathID == iPathID)
    {
      delete[] mPaths[i]->name;
      delete mPaths[i];

      mPaths.remove(i);
      return;
    }
  }
}

HierarchyPath*  Hierarchy::addPath(
  PCTSTR iName,
  INT iParentID
  )
{
  HierarchyPath* wPath = new HierarchyPath;
  BOOL wIDFound;

  _tcscpy(wPath->name = new TCHAR[_tcslen(iName) + 1], iName);

  wPath->parentID = iParentID;
  wPath->pathID = 1;

  do
  {
    wIDFound = FALSE;

    for (UINT i = 0; i < mPaths.getLength(); ++i)
    {
      if (wPath->pathID == mPaths[i]->pathID)
      {
        ++wPath->pathID;
        wIDFound = TRUE;
        break;
      }
    }
  }
  while (wIDFound);

  mPaths.add(wPath);

  return wPath;
}
